package guet.dream.common.freemarker;

import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Locale;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import freemarker.template.Configuration;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import guet.dream.common.util.StringUtil;

/***
 * <p> 最最基本的 MVC 控制器 Servlet基础类 .应用程序的Servlet控制器都应该继承此类. </p>
 * <div> <h2>映射方式,即访问处理:</h2>
 *   <p>
 *     设访问路径如下:http://sanyuan.dream.guet/tianyaclub/index/index.htm
 *     则映射处理的Servlet控制器位于guet.dream.tianyaclub包下的IndexAction类的indexAction方法.
 *      上面类名为在小写,路径为小写.
 *    </p>
 * </div>
 * @author banxi1988
 *
 */
public class BaseControllerServlet extends HttpServlet {
	
	private Logger log = LoggerFactory.getLogger(BaseControllerServlet.class);

	private Configuration cfg;
	private static final String PACKAGE_PREFIX = "guet.dream";
	private static final String CLAZZ_METHOD_SUFFIX = "Action"; 
	/***
	 * // 第一步:初始化FreeMaker配置并创建一个实例.
	 */
	@Override
	public void init(){
		
		cfg = new Configuration();
			//重要配置项(1):设置ftl模板存放所在地方. 注意是相对于getServletContext()对象的.
		cfg.setServletContextForTemplateLoading(getServletContext(), "WEB-INF/template");
		   
		   //重要配置项(2):设置模板更新的延时.开发模式下设置为0.
		cfg.setTemplateUpdateDelay(0);
		  
		   // 设置错误处理器,以便错误能够在浏览器上阅读.
		cfg.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
		  // 设置Bean包装器,推荐设置.
		cfg.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);
		 
		 //重要配置项(3) : 设置模板文件编码,输入编码.
		cfg.setDefaultEncoding("UTF-8");
		cfg.setOutputEncoding("UTF-8");
		 // 设置默认的区域.
		cfg.setLocale(Locale.CHINA);
		
		  // 设置时间数值显示格式. y表示年(year),M表示月(Month),d表示天(Day),其它你懂的.
       cfg.setNumberFormat("#.###");//数字格式化，最多保留三位小数
       cfg.setDateFormat("yyyy-MM-dd");
       cfg.setTimeFormat("HH:mm:ss");
       cfg.setDateTimeFormat("yyyy-MM-dd HH:mm:ss");
	}
	
	@Override
    protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
    	 doGet(request,response);
    }
	
	@Override
	protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
		String []names = resolveControllerName(request.getServletPath());
		String errorMssage;
		
		request.setCharacterEncoding("UTF-8");
		try {
			Class clazz = Class.forName(names[0]);
		    Object clazzInstance = clazz.newInstance();
			Method actionMethod = clazz.getMethod(names[1], HttpServletRequest.class);
		   actionMethod.invoke(clazzInstance, request);
		    Field pageField = clazz.getField("page");
		    Page page = (Page) pageField.get(clazzInstance);
		    
		   if(page.getTemplate()!= null	){
			   Template t = cfg.getTemplate(page.getTemplate());
			   response.setContentType("text/html;charset=UTF-8"	);
			   response.setHeader("Cache-Control", "no-store,no-cache,must-revalidate,"
					                 +"post-check=0,pre-check=0");
			   response.setHeader("Pragma", "no-cache");
			   response.setHeader("Expires", "Wed,02 Mar 1988 00:00:00 GMT");
			   
			   Writer out = response.getWriter();
			   
			   t.process(page.getRoot(),out);
		   }else if(page.getForward() != null){
			   RequestDispatcher view = request.getRequestDispatcher(page.getForward());
			   view.forward(request, response);
		   }else if(page.getRedirect()!= null){
			    response.sendRedirect(page.getRedirect());
		   }else{
			   throw new ServletException("Action处理器没有声明命令!");
		   }
		
		
		
		} catch (ClassNotFoundException e) {
			errorMssage = "控制器类加载失败,找不到此类:"+names[0];
			log.error(errorMssage);
			throw new ServletException(errorMssage);
		} catch (SecurityException e) {		
			throw new ServletException(e);
		} catch (NoSuchMethodException e) {
			errorMssage = names[0]+"类中找不到方法:"+names[1];
			log.error(errorMssage);
			throw new ServletException(errorMssage);
		} catch (IllegalArgumentException e) {
			throw new ServletException(e);
		} catch (IllegalAccessException e) {
			throw new ServletException(e);
		} catch (InvocationTargetException e) {
			throw new ServletException(e);
		} catch (TemplateException e) {
			throw new ServletException("处理Freemarker模板时出错!",e);
		} catch (InstantiationException e) {
			throw new ServletException("实例化类!"+names[1]+"出错!",e);
		} catch (NoSuchFieldException e) {
			throw new ServletException("反射获得字段值Page 数据模型出错,在类:"+names[1],e);
		}
		
	}
	

	/**
	 *  <p>
	 *  根据servletPath 解析得到相应控制器的全类名,和类中对应处理的方法名.
	 *  类所在包名根据包名前缀得到.在此类中定义
	 *  类名和方法名后缀在也类中有定义
	 *  </p>
	 * <p>对于下面的方法获得的path有不解的请参见.关于Servlet中getServlettPath与相关方法区别.txt
	 *  对于访问路径:设访问路径如下:http://sanyuan.dream.guet/tianyaclub/index/index.htm
	 *  得到的ServletPath为tianyaclub/index/index.htm
	 * </p>
	 * @param servletPath
	 * @return
	 * @author banxi1988
	 */
	protected String [] resolveControllerName(String servletPath){
	    String servletName = servletPath.split("\\.", 2)[0];//去除.htm后缀
	    String []paths = servletName.split("/");//如上面列子,正确情况paths={"","tianyaclub","index","index"},注意空字符串.
	    if(paths.length != 4){
	    	 log.error("路径映射错误:!"+servletName);
	     }
	    String shortClazzName = StringUtil.capitalize(paths[2]);
	    String clazzName = PACKAGE_PREFIX+"."+paths[1]+"."+shortClazzName+CLAZZ_METHOD_SUFFIX;
	    String methodName = paths[3]+CLAZZ_METHOD_SUFFIX;
	    log.info("URL访问响应信息,响应类名:"+clazzName+"类中处理方法名:"+methodName);
	    String names[] = {clazzName,methodName};
	    
		 return names;
	}

}
